//*** Guidance generated code ***//
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics.Contracts;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Caching;
using System.Xml;
using System.Xml.Linq;

using NHibernate;

using ProjectBase.Core;
using ProjectBase.Core.Utils;
using ProjectBase.Core.Dao;

using Configuration = NHibernate.Cfg.Configuration;
using Environment = System.Environment;

namespace ProjectBase.Data.NH
{
    /// <summary>
    ///     NHibernate Helper class for managing sessions, flushing session data,
    ///     initializing Session Factory, etc.
    /// </summary>
    public static class NHSessionFactory
    {
        #region members
        static Configuration _configuration;
        static INHRequestSession _storage;
        #endregion members

        #region Initialize
        /// <summary>
        ///     Enter point for NHibernate initialization, which in fact provides the INHRequestSession implementer.
        ///     Provided storage is then used for ISession storage (e.g. Request.Item cache for web).
        ///     Storage is saved in a statci cache and therefore can survive Factory breakdowns and Disposing.
        ///     That allows the Application Factory Recycling after DaoException occures
        /// </summary>
        /// <param name = "storage">The storage.</param>
        public static void Initialize(INHRequestSession storage)
        {
            _storage = storage;
        }
        #endregion Initialize

        /// <summary>A list of names instantiated Factories</summary>
        public static IEnumerable<string> FactoryNames { get { return Instance.Factories.Keys; } }

        /// <summary>Gets or Opens the ISession, using passed Factory name.</summary>
        public static ISession GetCurrentSession(string factoryName)
        {
            var session = _storage.GetSession(factoryName) as ISession;

            if (session.IsNull() || !session.IsOpen)
            {
                session = Instance.Factories[factoryName].OpenSession();
                session.CacheMode = CacheMode.Normal;
                session.FlushMode = FlushMode.Commit;
                _storage.SetSession(factoryName, session);
            }
            return session;
        }

        public static ISessionFactory GetFactory(string factoryName = Const.DefaultFactory)
        {
            ISessionFactory factory;
            if (Instance.Factories.TryGetValue(factoryName, out factory))
            {
                return factory;
            }
            throw new KeyNotFoundException("Configured ISessionFactory collection is missing key: {0}".FormatWith(factoryName));
        }


        #region Instance Factories
        /// <summary>
        ///     Lazily loaded Singeton
        /// </summary>
        static class Instance
        {
            internal static readonly IDictionary<string, ISessionFactory> Factories = CreateSessionFactories();

            #region BuildFactory
            /// <summary>
            /// Loads all NHibernate.config files listed in Project.config, builds Factories
            /// </summary>
            /// <returns></returns>
            static IDictionary<string, ISessionFactory> CreateSessionFactories()
            {
                var factories = new SortedList<string, ISessionFactory>();

                foreach (var filePath in ReadConfigFilePath())
                {
                    // configuration file path from project.config
                    XDocument document = XDocument.Load(filePath);

                    // <session-factory name="Default">
                    var attr = document.Root
                        .Element(Const.NHibernateNs + Const.SessionFactory)
                        .Attribute(Const.Name);
                    var name = attr.Value;

                    var factory = CreateSessionFactory(document);
                    factories[name] = factory;
                }
                return factories;
            }
            /// <summary>
            ///     Creates the session factory.
            /// </summary>
            /// <returns></returns>
            static ISessionFactory CreateSessionFactory(XDocument document)
            {
                Contract.Requires(document.Is(), "NHibernate configuration file missing");

                _configuration = new Configuration();
                if (_configuration.IsNull())
                {
                    throw new InvalidOperationException("NHibernate configuration is null.");
                }

                using (var reader = document.CreateReader())
                {
                    _configuration.Configure(reader);
                }

                var factory = _configuration.BuildSessionFactory();
                if (factory.IsNull())
                {
                    throw new InvalidOperationException("Call to Configuration.BuildSessionFactory() returned null.");
                }
                return factory;
            }
            /// <summary>
            ///     Gets the config file path.
            /// </summary>
            /// <returns></returns>
            static IEnumerable<string> ReadConfigFilePath()
            {
                foreach (var filePath in Global.Settings.ConfigurationFilePath.NHibernateFiles)
                {
                    var fileName = Global.CreateAbsolutePath(filePath);

                    if (!File.Exists(fileName))
                    {
                        throw new FileNotFoundException(
                            "Cannot find NHibernate file. Searched relative {0} and absolute {1} locations.".FormatWith(fileName, filePath));
                    }
                    yield return fileName;
                }
            }

            #endregion BuildFactory
        }
        #endregion Instance Factories

        #region Dispose
        public static void DisposeCurrentSession(string factoryName)
        {
            var session = _storage.GetSession(factoryName) as ISession;
            if (session.IsNull())
            {
                return;
            }
            session.Close();
            session.Dispose();
            _storage.SetSession(factoryName, null);
        }
        #endregion Dispose
    }
}